<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>navigator</title>
    <link rel="icon" href="{{ url_for('static',filename='img/favicon.ico') }}">
    <link rel="stylesheet" href="{{ url_for('static',filename='css/w3.css') }}" />
    <link rel="stylesheet" type="text/css"
        href="{{ url_for('static', filename='font-awesome-4.7.0/css/font-awesome.min.css')}}">

    <script type="text/javascript" src="{{ url_for('static', filename='lib/EaselJS/lib/easeljs.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='lib/eventemitter2.min.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='lib/roslibjs/build/roslib.min.js') }}"></script>

    <script type="text/javascript" src="{{ url_for('static', filename='lib/require.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='lib/ros2d.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='lib/LaserShape.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='lib/LaserShapeClient.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='lib/NavGoal.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='lib/NavPath.js') }}"></script>
    <script type="text/javascript" src="{{ url_for('static', filename='lib/PoseAndTrace.js') }}"></script>

    <style>
    .modal {
        display: none;
        position: fixed;
        z-index: 1;
        left: 0;
        top: 0;
        width: 100%;
        height: 100%;
        overflow: auto;
        background-color: rgba(0,0,0,0.4);
    }

    .modal-content {
        background-color: #fff;
        margin: 15% auto;
        padding: 20px;
        border: 1px solid #888;
        width: 80%;
        max-width: 600px;
        box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
        animation: fadeIn 0.4s;
    }

    .close {
        color: #aaa;
        float: right;
        font-size: 28px;
        font-weight: bold;
    }

    .close:hover,
    .close:focus {
        color: black;
        text-decoration: none;
        cursor: pointer;
    }
    @keyframes fadeIn {
        from {opacity: 0;}
        to {opacity: 1;}
    }
    </style>
   <!--  var actionParam = {
        key: 'stationType',
        value: 'floor',
        valueDataType: 'useless'
    };

    var action = {
        actionType: '',
        actionId: 0,
        actionDescription: '-',
        blockingType: 'HARD',
        actionParameters: []
    } -->
    <script type="text/javascript" type="text/javascript">

        var MAP_WIDTH = (window.innerWidth)*0.8;
        var MAP_HEIGHT = (window.innerHeight)*1.4;
        var isEditMode = false;     //编辑模式
        var isLineMode = false;     //直线模式
        var isCurveMode = false;    //直线模式
        var isUndoMode = false;
        var isbackup = false;
        var rackFollowUp = false;
        var vehicleYaw = 0;
        var lastYaw = 0;
        var orderId = 0;
        var lastPoint = 0;
        var isIdle = true;
        var isDriving = false;
        var targetGoal;
        var taskList = [];
        var targetId = 0;
        var targetAction = '';

        var points = {};
        var paths = [];

        var CurveObj;
        var mapList;

        function init() {
            var ros = new ROSLIB.Ros({
                url: 'ws://' + window.location.hostname + ':19000'
            });

            ros.on('error', function (error) {
                console.log(error);
                var state = document.getElementById("connect");
                state.innerHTML = "Error!";
                state.style.color = "red";
            });
            // Find out exactly when we made a connection.
            ros.on('connection', function () {
                console.log('Connection made!');
                var state = document.getElementById("connect");
                state.innerHTML = "Conneted!";
                state.style.color = "green";
            });

            ros.on('close', function () {
                console.log('Connection closed.');
                var state = document.getElementById("connect");
                state.innerHTML = "Close!";
                state.style.color = "yellow";
            });

            //创建一个观察器，在map中进行显示
            var mapViewer = new ROS2D.Viewer({
                divID: 'map',
                width: MAP_WIDTH,
                height: MAP_HEIGHT
            });
            //当鼠标与多边形发生互动时的回调函数
            var clickedPolygon = false;
            var selectedPointIndex = null;

            function normalizeAngle(angle) {
                angle = angle % 360;
 
                if (angle >= 180) {
                    return angle - 360;
                }
             
                if (angle < -180) {
                    return angle + 360;
                }
                return angle;
            };

            function dijkstra(points, paths, start, end) {
                const graph = {};
                for (let pointId in points) {
                    graph[pointId] = [];
                }

                for (let path of paths) {
                    graph[path.start].push({ cost: path.cost, point: path.end });
                    // graph[path.end].push({ cost: path.cost, point: path.start }); // 如果路径是双向的
                }

                const queue = [{ cost: 0, point: start, path: [] }];
                const seen = new Set();
                const minDist = { [start]: 0 };

                while (queue.length > 0) {
                    queue.sort((a, b) => a.cost - b.cost);      // 模拟优先队列
                    const { cost, point: currentPoint, path } = queue.shift();

                    if (seen.has(currentPoint)) {
                        continue;
                    }

                    const newPath = path.concat(currentPoint);
                    seen.add(currentPoint);
                   
                    if (currentPoint === end) {
                        return { cost, path: newPath };
                    }

                    for (let neighbor of graph[currentPoint]) {
                        if (seen.has(neighbor.point)) {
                            continue;
                        }
                       
                        const nextDist = cost + neighbor.cost;
                        
                        if (minDist[neighbor.point] === undefined || nextDist < minDist[neighbor.point]) {
                            minDist[neighbor.point] = nextDist;
                            queue.push({ cost: nextDist, point: neighbor.point, path: newPath });
                        }
                    }
                }

                return { cost: Infinity, path: [] };
            }

            function openDialog() {
                document.getElementById('orderDialog').style.display = 'block';
                document.getElementById('orderTitle').innerHTML = "导航到点:" + targetId;
            }

            function closeDialog() {
                document.getElementById('orderDialog').style.display = 'none';
            }

            function submitOrder() {
                var target = isDriving ? targetGoal : lastPoint;
                if (isIdle)
                    taskList = [];
                var findPath = dijkstra(points, paths, target, targetId);
               
                if (findPath.path.length > 0) {
                    var nodes = [];
                    targetAction = document.getElementById('actionSelect').value;
                    let actionParam = document.getElementById('actionParam').value;
                    
                    for (let i=0;i<findPath.path.length;i++) {
                        if (lastPoint === findPath.path[i] || targetGoal === findPath.path[i]) {
                            continue;
                        }
                        var actionParams = [];
                        var actions = [];

                        if (targetAction != 'none' && i === (findPath.path.length - 1)) {
                            let action = {};
                            action.actionType = (targetAction === 'load'? '1' : '2');
                            action.actionId = '0';
                            action.actionDescription = '-';
                            action.blockingType = 'HARD';
                            // action.actionParams = [];
                            actions.push(action);
                        }
                        
                        node = {
                                nodeId: findPath.path[i],
                                sequenceId: 0,
                                nodeDescription: '-',
                                released: false,
                                actions: actions
                            };
                        taskList.push(findPath.path[i]);
                        nodes.push(node);
                    }
                    // console.log("nodes", nodes);
                    var currentTimestamp = new Date().toISOString();
                    var request = new ROSLIB.ServiceRequest({
                        recycle: false,
                        order: {
                            timestamp: currentTimestamp,
                            serialNumber: '1',
                            orderId: orderId.toString(),
                            orderUpdateId: 0,
                            zoneSetId: targetId ,
                            nodes: nodes
                        }
                    });

                    orderService.callService(request, function(response) {
                        console.log('Service call succeeded: ', response);
                        if (response.success) {
                            alert("任务:" + orderId + "命令发送成功！");
                        } else {
                            alert("任务:" + orderId + "失败," + response.message);
                        }
                        orderId = orderId + 1;
                    }, function(error) {
                        console.log('Service call failed: ', error);
                        alert("命令发送失败！");
                        taskList = [];
                    });
                } else {
                    alert("找不到路径！");
                }

                closeDialog();
            }

            document.getElementById('submitButton').addEventListener('click', function() {
                submitOrder();
            });

            document.getElementById('cancelButton').addEventListener('click', function() {
                closeDialog();
            });
            //点的绘制
            var pointCallBack = function (type, event, index) {
                if (type === 'mousedown') {                         //检查鼠标是否被按下
                    if (event.nativeEvent.shiftKey === true) {      //指示当事件发生时，shift 键是否被按下并保持住。
                        if (isEditMode) {                           //判断是否在编辑模式下,自定义变量，初始值为flase，
                            polygon.remPoint(index);                //从多边形中删除索引的点
                            console.log("remport");
                            CurveObj = polygon.getCurvesObj();      //获取线的json数据，CurveObj自定义变量, 数组。  ？？？？？作用
                        }
                    } else if (button === 2) {
                        targetId = polygon.pointContainer.getChildAt(index+1).name;
                        // var r = confirm("导航到点:" + targetId + "?");
                        // if (r) {
                            openDialog();
                        // }
                    }
                    else {                          //shift没有按下，输出点的信息
                        selectedPointIndex = index; //点的索引
                        document.getElementById("showLine1").style.display = "none"; //获取当前页面里面id为showline的标签，位于网页右端，改变该标签的样式，使其不显示。
                        document.getElementById("showLine2").style.display = "none";
                        var msg = document.getElementById("showPoint");             //Document的方法 getElementById()返回一个匹配特定ID的元素，此处为点的信息
                        var yaw = polygon.rotationJudge(polygon.pointContainer.getChildAt(index).rotation);     //获取容器中点的索引，进行旋转。此处的作用？？？？
                        msg.style.display = "block";                                //改变该标签的样式，使其满眶显示。
                        document.getElementById("pid").innerHTML = "Point ID:" + polygon.pointContainer.getChildAt(index+1).name;
                        document.getElementById("px").innerHTML = "X:" + Math.round(polygon.pointContainer.getChildAt(index).x * 100) / 100;    //Math.round() 函数返回一个数字四舍五入后最接近的整数。
                        document.getElementById("py").innerHTML = "Y:" + Math.round(polygon.pointContainer.getChildAt(index).y * 100) / 100;
                        document.getElementById("pyaw").innerHTML = "YAW:" + Math.round(yaw * 100) / 100;
                    }
                }
                clickedPolygon = true;
            };  

            var getOpposite = function (index) {
                var isBidirection = false;
                var oppositeId = 0; 
                var start = CurveObj[index / 2].startPoint.num;
                var end = CurveObj[index / 2].endPoint.num;
                
                for (let i=0;i<CurveObj.length;i++) {
                    var sp = CurveObj[i].startPoint.num;
                    var ep = CurveObj[i].endPoint.num;
                   
                    if (sp === end && ep === start) {
                        oppositeId = i;
                        isBidirection = true;
                        break;
                    }
                }
                return { isBidirection: isBidirection, oppositeId: oppositeId };
            };

            var showMsg = function(index, n) {
                var shownum = "showLine" + n;
                var msg = document.getElementById(shownum);
                var start = CurveObj[index / 2].startPoint;
                var end = CurveObj[index / 2].endPoint;
                var yaw1 = CurveObj[index / 2].startPoint.rotation;
                var yaw2 = CurveObj[index / 2].endPoint.rotation;
                var cp1 = CurveObj[index / 2].cp1;
                var cp2 = CurveObj[index / 2].cp2;

                var startPoint = CurveObj[index / 2].startPoint.num;
                var endPoint = CurveObj[index / 2].endPoint.num;
               
                msg.style.display = "block";                //页面右端线段信息元素呈现为块级元素。                        
                document.getElementById("pathid" + n).innerHTML = CurveObj[index / 2].mode + " ID:" + CurveObj[index / 2].index;        
                document.getElementById("p" + n + "vel").value = CurveObj[index / 2].velocity;
                document.getElementById("p" + n + "id1").innerHTML = "Start Point:" + startPoint;
                document.getElementById("p" + n + "id2").innerHTML = "End Point:" + endPoint;
                document.getElementById("p" + n + "x1").innerHTML = "Start X: " + Math.round(CurveObj[index / 2].startPoint.x * 100) / 100;
                document.getElementById("p" + n + "y1").innerHTML = "Start Y: " + Math.round(CurveObj[index / 2].startPoint.y * 100) / 100;
                document.getElementById("p" + n + "yaw1").innerHTML = "Start YAW: " + Math.round(yaw1 * 100) / 100;
                document.getElementById("p" + n + "x2").innerHTML = "End X: " + Math.round(CurveObj[index / 2].endPoint.x * 100) / 100;
                document.getElementById("p" + n + "y2").innerHTML = "End Y: " + Math.round(CurveObj[index / 2].endPoint.y * 100) / 100;
                document.getElementById("p" + n + "yaw2").innerHTML = "End YAW: " + Math.round(yaw2 * 100) / 100;
                document.getElementById("segment" + n).innerHTML = "segment: " + CurveObj[index / 2].segment;
                document.getElementById("cp" + n + "x1").innerHTML = "CP1 X: 0" + Math.round(cp1.x * 100) / 100;
                document.getElementById("cp" + n + "y1").innerHTML = "CP1 Y: 0" + Math.round(cp1.y * 100) / 100;
                document.getElementById("cp" + n + "x2").innerHTML = "CP2 X: 0" + Math.round(cp2.x * 100) / 100;
                document.getElementById("cp" + n + "y2").innerHTML = "CP2 Y: 0" + Math.round(cp2.y * 100) / 100;
            };

            var showLineMsg = function(index) {
                document.getElementById("showPoint").style.display = "none";    //页面右端点信息不显示 
                showMsg(index, 1);
                var result = getOpposite(index);
              
                if (result.isBidirection) {
                    showMsg(result.oppositeId * 2, 2);
                } else {
                    document.getElementById("showLine2").style.display = "none";
                }   
            };

            //线的绘制
            var lineCallBack = function (type, event, index) {
                if (type === 'mousedown') {                         //在下面定义，鼠标动作。
                    if (event.nativeEvent.ctrlKey === true && isEditMode) {       //Ctrl 键是否被按下并保持住。ctrlKey 事件属性可返回一个布尔值，指示当事件发生时，
                        polygon.splitLine(index);                   //分割多边形的一条线：在该线的中心插入一个点
                    } else {                                        //Ctrl键没有被按下，在页面右端显示信息
                        console.log("linecallback", index);
                        showLineMsg(index); 
                    }; 
                };
                clickedPolygon = true;
            };

            //曲线调用
            var curveCallBack = function (type, event, index) {
                if (type === 'mousedown') {
                    if (event.nativeEvent.ctrlKey === true) {
                        
                    } else {  
                        console.log("curveCallBack", index);                         //ctrl键没有按住保持
                        // polygon.bezierCPointContainer.removeAllChildren();           //移除所有的曲线
                        showLineMsg(index);
                      
                        // if (CurveObj[index / 2].segment == "Fwd") {
                        //     polygon.createBezierCPShape(start, cp1);        
                        //     polygon.createBezierCPShape(end, cp2);
                        // };
                    };
                };
                clickedPolygon = true;              //后续用于监听鼠标是否动作
            };
            
            // Create the polygon，绘制多边型
            var polygon = new ROS2D.PolygonMarker({
                lineColor: createjs.Graphics.getRGB(100, 100, 255, 1),
                pointSize: 0.1,
                pointCallBack: pointCallBack,       //点，线，曲线与鼠标互动的回调函数，通过调用polygon.pointcallback
                lineCallBack: lineCallBack,
                curveCallBack: curveCallBack
            });
            // 鼠标与舞台互动的事件监听器
            mapViewer.scene.mouseMoveOutside = false; 
            // Add zoom to the viewer，为查看器添加缩放功能。
            var zoomView = new ROS2D.ZoomView({
                rootObject: mapViewer.scene
            });

            // 在视图中添加平移功能
            var panView = new ROS2D.PanView({
                rootObject: mapViewer.scene
            });

            //听取路径信息并绘制路径。
            var plannedPath = new ROS2D.NavPath({
                ros: ros,
                rootObject: mapViewer.scene,
                pathTopic: '/plan'
            });
           
            //听取姿势信息，绘制机器人的姿势和轨迹
            var robotTrace = new ROS2D.PoseAndTrace({
                ros: ros,
                traceSize: 0.5,
                robotSize: 10,
                robotColor: createjs.Graphics.getRGB(76, 153, 0, 0.66),
                rootObject: mapViewer.scene,
                withTrace: true,
                maxTraceLength: 500
            });    

            mapViewer.scene.addChild(robotTrace.robotMarker);
            mapViewer.scene.addChild(robotTrace.trace);
            mapViewer.scene.addChild(robotTrace.peripheral);

            //监听里程计信息
            var velListener = new ROSLIB.Topic({
                ros: ros,
                name: '/odom',
                messageType: 'agv_msgs/msg/OdometryLite'
                //throttle_rate: 500
            });

            var orderService = new ROSLIB.Service({
                ros: ros,
                name: '/local_order',
                serviceType: 'agv_srvs/msg/LocalOrder'
            });

            //里程计信息的订阅与显示
            velListener.subscribe(function (message) {
                var x = message.twist.linear.x;
                var y = message.twist.linear.y;
                var a = message.twist.angular.z;

                vehicleYaw = Math.round(robotTrace.robotMarker.stage.rosQuaternionToGlobalTheta(message.pose.orientation)*100);
                robotTrace.peripheral.angle = normalizeAngle(robotTrace.peripheral.angle);
                if (rackFollowUp) {
                    var temp = normalizeAngle(vehicleYaw/100 - lastYaw);
                    if (temp > 90) temp = 90;
                    else if (temp < -90) temp = -90;
                    var angle = normalizeAngle(robotTrace.peripheral.angle - temp);
                    lastYaw = vehicleYaw/100;
                    robotTrace.peripheral.rotationAngle(angle);
                } else {
                    const remainder = robotTrace.peripheral.angle % 90;
                    if (Math.abs(remainder) > 0.05) {
                        temp = 90 - Math.abs(remainder);

                        adjustmentAngle = (Math.abs(remainder) > 45) ? (remainder > 0 ? temp : -temp) : 
                                            (remainder > 0 ? -remainder : remainder);              
                        console.log(temp, remainder, adjustmentAngle);
                        
                        robotTrace.peripheral.angle = robotTrace.peripheral.angle + adjustmentAngle;
                        console.log(robotTrace.peripheral.angle);
                    }
                }

                //信息显示
                document.getElementById("velocity").innerHTML = "速度:" + Math.round(x * 100) / 100 +
                    ":" + Math.round(y * 100) / 100 + ":" + Math.round(a * 100) / 100;
                document.getElementById("rack").innerHTML = "托盘:" + Math.round(robotTrace.peripheral.rotation * 100) / 100;
            }); 
            // rack follow up
            var autoStatusListener = new ROSLIB.Topic({
                ros: ros,
                name: '/auto_run_status',
                messageType: 'agv_msgs/msg/AgvAutoStatus'
            });

            autoStatusListener.subscribe(function(message){
                lastPoint = message.fromNode;
                isIdle = message.idle;
                isDriving = message.driving;
                targetGoal = message.nodeIdNew;
                currentTask = message.toNode;
                percent = message.fProgPercent;

                if (rackFollowUp !== message.rackFollowUp) {
                    rackFollowUp = message.rackFollowUp;
                    if (rackFollowUp) {
                        lastYaw = normalizeAngle(vehicleYaw/100); 
                    }
                }

                document.getElementById("taskCount").innerHTML = "数量:" + taskList.length;
                document.getElementById("tasklist").innerHTML = "任务:"  + taskList;
                document.getElementById("curTask").innerHTML = "当前:" + currentTask + "(" + Math.round(percent*100) + "%)";
                document.getElementById("taskAction").innerHTML = "动作:" + targetAction;
            });

            // load state
            var peripheralListener = new ROSLIB.Topic({
                ros: ros,
                name: '/periphStatus',
                messageType: 'agv_msgs/msg/PeriphStatus'
            });
            peripheralListener.subscribe(function(message){
                robotTrace.peripheral.updateStatus(message.loaded);
                showload = (message.loaded > 0) ? "负载：满载" : "负载：空载"
                document.getElementById("load").innerHTML = showload;
            });

            //位姿话题监听与订阅
            var poseListener = new ROSLIB.Topic({
                ros: ros,
                name: '/pose',
                messageType: 'agv_msgs/msg/PoseWithConfidence'
                //throttle_rate: 500
            });

            poseListener.subscribe(function (message) {
                // 增加机器人位姿
                var robotPos = new ROSLIB.Pose();
                robotPos.position.x = message.pose.pose.position.x;
                robotPos.position.y = message.pose.pose.position.y;
                robotPos.orientation.z = message.pose.pose.orientation.z;
                robotPos.orientation.w = message.pose.pose.orientation.w;
                //位姿轨迹的绘制更新
                robotTrace.updatePose(robotPos);
                // mapViewer.scene.addChild(robotTrace.trace);
                // mapViewer.scene.addChild(robotTrace.robotMarker);
                // mapViewer.scene.addChild(robotTrace.peripheral);
                mapViewer.scene.update();

                // console.log("robotMarker", robotTrace.robotMarker)
                var yaw =  Math.round(robotTrace.robotMarker.stage.rosQuaternionToGlobalTheta(robotPos.orientation) * 100);
                document.getElementById('vehiclePose').innerHTML = "位置:" + Math.round(robotPos.position.x * 100) / 100
                    + ":" + Math.round(robotPos.position.y * 100) / 100 + ":" + yaw / 100;

                document.getElementById('score').innerHTML = "得分：" + message.score;
            });

            //导航目标的设置
            var navGoal = new ROS2D.NavGoal({
                ros: ros,
                rootObject: mapViewer.scene
            });

            var goalPose = new ROSLIB.Pose();
            // Add Pose Estimate topic，位姿估计话题
            var topicPoseEstimate = new ROSLIB.Topic({
                ros: ros,
                rootObject: mapViewer.scene,
                name: '/initialpose',
                messageType: 'geometry_msgs/msg/PoseWithCovarianceStamped'
            });

            // Add Scan topic，监听激光雷达的扫描
            var laserClient = new ROS2D.LaserShapeClient({
                ros: ros,
                rootObject: mapViewer.scene,
                inversion: false         //注意此处的激光数据反转，默认为true
            });

            // 监听栅格地图话题，默认/map
            var mapGridClient = new ROS2D.OccupancyGridClient({
                ros: ros,
                rootObject: mapViewer.scene,
                continuous: true
            });

            // Scale the canvas to fit to the map
            mapGridClient.on('change', function () {
                gridWidth = mapGridClient.currentGrid.width;
                gridHeigh = mapGridClient.currentGrid.height;
                mapViewer.scaleToDimensions(gridWidth, gridHeigh);
                grid_pose_x = mapGridClient.currentGrid.pose.position.x;
                grid_pose_y = mapGridClient.currentGrid.pose.position.y;
                mapViewer.shift(grid_pose_x, grid_pose_y);
                plannedPath.initScale();
                robotTrace.initScale();
                navGoal.initScale();
            });

            // Setup mouse event handlers，设置鼠标事件的处理
            var mouseDown = false;
            var zoomKey = false;
            var panKey = false;
            var isPoseEstimated = false;
            var startPos = new ROSLIB.Vector3();        //创建3维 vector对象，有xyz属性，并可进行不同函数处理
            var showDrawmap = false;
            var button;

            // EVENT: Mouse DOWN，按下鼠标即发生
            mapViewer.scene.addEventListener('stagemousedown', function (event) {
                if (event.nativeEvent.ctrlKey === true) {               //ctrl+鼠标
                    console.log("Ctrl + Mouse");                       
                    zoomKey = true;
                    zoomView.startZoom(event.stageX, event.stageY);     //确定缩放的中心
                }
                else if (event.nativeEvent.shiftKey === true) {         //shift+鼠标
                    console.log("Shift + Mouse");                       
                    panKey = true;
                    panView.startPan(event.stageX, event.stageY);       //确定平移的中心
                }
                else {
                    var pos = mapViewer.scene.globalToRos(event.stageX, event.stageY);  //将给定的世界坐标转换位ROS坐标
                    console.log("x: "+pos.x+" y: " + pos.y);        //此处是鼠标坐标

                    if (event.nativeEvent.altKey === true) {        //alt+鼠标
                        console.log("Alt + Mouse");         
                        navGoal.startGoalSelection(pos);   //位姿的估计，开始选择目标，给定的位置将是目标位置，绘制方向标记。
                        isPoseEstimated = true;     
                    }
                    else {                                 //仅是鼠标
                        console.log("Mouse");               
                        navGoal.startGoalSelection(pos);   //开始目标选择，给定的位置将是目标位置，绘制方向标记
                    }
                }
                button = event.nativeEvent.button;
                startPos.x = event.stageX;          //点的坐标赋值
                startPos.y = event.stageY;
                mouseDown = true;
            });
            
            // EVENT: Mouse MOVE，     mousemove 事件是一个实时响应的事件，当鼠标指针的位置发生变化时（至少移动一个像素），就会触发 mousemove 事件。
            mapViewer.scene.addEventListener('stagemousemove', function (event) {
                if (mouseDown === true) {           //鼠标被按下
                    if (zoomKey === true) {         //ctrl+鼠标
                        var dy = event.stageY - startPos.y;
                        var zoom = 1 + 10 * Math.abs(dy) / mapViewer.scene.canvas.clientHeight;
                        if (dy < 0)
                            zoom = 1 / zoom;
                        zoomView.zoom(zoom);        //进行缩放
                    }
                    else if (panKey === true) {     //shift+鼠标
                        panView.pan(event.stageX, event.stageY);       //平移
                    }
                    else {
                        var pos = mapViewer.scene.globalToRos(event.stageX, event.stageY);         //将给定的全局Stage坐标转换为ROS坐标
                        navGoal.orientGoalSelection(pos);                                          //定位目标，在开始目标后，这个函数更新目标定位标记的方向。单位为米
                        if (selectedPointIndex !== null && isEditMode) {                           //点的索引不为空，也在编辑模式下
                            var pos = mapViewer.scene.globalToRos(event.stageX, event.stageY);     //将给定的全局Stage坐标转换为ROS坐标
                            polygon.movePoint(selectedPointIndex, pos);                            //移动多边形的一个点，索引和点
                            CurveObj = polygon.getCurvesObj();                                     //get line json data
                        }
                    }
                }
            });
            
            // EVENT: Mouse UP   mouseup在元素上释放指针设备按钮时会触发该事件。  鼠标释放
            mapViewer.scene.addEventListener('stagemouseup', function (event) {
                if (mouseDown === true) {       //鼠标按下
                    if (zoomKey === true) {     //缩放
                        zoomKey = false;        //缩放停止
                    }
                    else if (panKey === true) {
                        panKey = false;         //平移停止
                    }
                    else {
                        var pos = mapViewer.scene.globalToRos(event.stageX, event.stageY);      //转换到ros坐标系下
                        // Add point when not clicked on the polygon
                        if (selectedPointIndex !== null) {                  //选择的点的索引不为空
                            selectedPointIndex = null;
                        }
                        else if (mapViewer.scene.mouseInBounds === true && clickedPolygon === false) {     //鼠标是否在区域内，鼠标是否在进行点，线，曲线的调用
                            polygon.bezierCPointContainer.removeAllChildren();
                            goalPose = navGoal.endGoalSelection(pos);

                            var goalPolygon = navGoal.goalOrientationMarker;
                            startPos.x = goalPose.position.x;
                            startPos.y = goalPose.position.y;
                            startPos.rotation = polygon.rotationJudge(navGoal.stage.rosQuaternionToGlobalTheta(goalPose.orientation));
                            document.getElementById("Pose").innerHTML = "鼠标(x:" + Math.round(startPos.x * 100) / 100 + " y:" + Math.round(startPos.y * 100) / 100 + ")";
                            console.log("startPos.x: " + startPos.x);
                            console.log("startPos.y: " + startPos.y);
                            console.log("goalPose.orientation.z: " + goalPose.orientation.z);
                            console.log("goalPose.orientation.w: " + goalPose.orientation.w);
                            if (isPoseEstimated === true) {                             //位姿估计
                                console.log("map.js-228-Pose Estimate!");
                                var covariance;
                                var msgPoseEstimate = new ROSLIB.Message({
                                    header: { stamp: {
                                                sec: 0,
                                                nanosec: 0,
                                            }, frame_id: 'map', },
                                    pose: {                                             //定的位置将是目标位置，绘制方向标记
                                        pose: goalPose,
                                        covariance: covariance,
                                    },
                                });
                                
                                topicPoseEstimate.publish(msgPoseEstimate);
                                isPoseEstimated = false;
                            } else if (isEditMode) {                                    //编辑模式下
                                console.log("map.js-243-Nav Simple Goal!");
                                navGoal.goalMarker(goalPose);
                                var mode = isLineMode ? 0 : (isCurveMode ? 1 : 100);
                                polygon.addPointPath(startPos, mode, 2);
                                mapViewer.scene.addChild(goalPolygon);
                                mapViewer.scene.addChild(polygon);
                                CurveObj = polygon.getCurvesObj();
                            }
                        }
                        clickedPolygon = false;
                    }
                    mouseDown = false;
                }
            });
            
            //下载路线
            function LoadCurveObj(showMessage = true) {
                console.log("Load path.xml");      //console.log() 方法用于在控制台输出信息。
                var XHR = new XMLHttpRequest(); //XMLHttpRequest（XHR）对象用于与服务器交互。通过XMLHttpRequest可以在不刷新页面的情况下请求特定URL，获取数据。
                XHR.open('GET', "load_path?name=path.xml", true);
                XHR.send();                     //将请求发送到服务器，用于 GET 请求
                XHR.onreadystatechange = function () {      //定义当 readyState 属性发生变化时被调用的函数
                    if (XHR.readyState === 4 && XHR.status === 200) {       //4：请求已完成且响应已就绪，200: "OK"
                        var res = XHR.responseText;         //XMLHttpRequest.responseText属性返回从服务器接收到的字符串，该属性为只读
                        var obj = eval("(" + res + ")");    //obj对象不是字符串。eval()函数用来执行一表达式，并返回表达式的值。                       
                        var json_obj = JSON.parse(obj);
                        var item = json_obj.PATH.item;      //此处的PATH.item?item应该是一个数组。
                        console.log(item);                  //6个路径信息
                        if (item) {
                            var length = json_obj.PATH.item.length;     
                            console.log(length);            //字符串的长度,6
                            if (showMessage)                            //自定义变量
                                polygon.removeAllPoint(polygon);
                                console.log(CurveObj);        //移除所有的点
                            if (length > 0) {                           //长度不为0
                                console.log(item[0]);                   //输出路径信息
                                startPos.x = item[0].startPoint.x;      //点的数据赋值
                                startPos.y = item[0].startPoint.y;
                                startPos.rotation = item[0].startPoint.rotation;                               
                                polygon.addPointPath(startPos, (item[0].mode == "line") ? 0 : 1, item[0].velocity);   //0是直线，1为曲线，进行闭合                               
                                mapViewer.scene.addChild(polygon);  //增加显示
                                for (i = 0; i < length; i++) {      //对其长度进行遍历
                                    console.log(item[i]);
                                    if (item[i].segment == "Fwd") {
                                        console.log("Fwd path");
                                        polygon.lineColor = createjs.Graphics.getRGB(100, 100, 255, 1);
                                        polygon.lineSize = 0.05;
                                        startPos.x = item[i].endPoint.x;
                                        startPos.y = item[i].endPoint.y;
                                        startPos.rotation = item[i].endPoint.rotation;
                                        if (startPos.x != item[0].startPoint.x || startPos.y != item[0].startPoint.y) {     
                                            console.log("start");
                                            polygon.addPointPath(startPos, (item[i].mode == "line") ? 0 : 1, item[i].velocity);  //0是直线，1为曲线，进行闭合                                       
                                            mapViewer.scene.addChild(polygon);
                                        } else {
                                            console.log("endloop");
                                            polygon.addEndLooptPath((item[i].mode == "line") ? 0 : 1, item[i].velocity);      //0是直线，1为曲线，进行闭合
                                        }
                                    } else {
                                        console.log("进入后退路径编辑");
                                        polygon.lineColor = createjs.Graphics.getRGB(0, 255, 0);
                                        polygon.lineSize = 0.02;
                                        if (item[i].mode == "line") {
                                            console.log("进入后退直线");
                                            
                                            var loadstartPos = new ROSLIB.Vector3();
                                            loadstartPos.x = item[i].startPoint.x;
                                            loadstartPos.y = -item[i].startPoint.y;   
                                            loadstartPos.rotation = item[i].startPoint.rotation;     

                                            var loadendPos = new ROSLIB.Vector3();
                                            loadendPos.x = item[i].endPoint.x;
                                            loadendPos.y = -item[i].endPoint.y;   
                                            loadendPos.rotation = item[i].endPoint.rotation; 

                                            var pointnum = item[i].endPoint.num;
                                            var lineback = polygon.createLineShape(loadstartPos, loadendPos);
                                            var linebacktext = polygon.createBackLineText(loadstartPos, loadendPos, item[i].velocity, pointnum);
                                            polygon.lineContainer.addChild(lineback, linebacktext);
                                            polygon.drawFill();
                                            mapViewer.scene.addChild(polygon);
                                        } else {
                                            console.log("进入后退曲线");
                                            
                                            var loadstartPos = new ROSLIB.Vector3();
                                            loadstartPos.x = item[i].startPoint.x;
                                            loadstartPos.y = -item[i].startPoint.y;
                                            loadstartPos.rotation = item[i].startPoint.rotation;

                                            var loadendPos = new ROSLIB.Vector3();
                                            loadendPos.x = item[i].endPoint.x;
                                            loadendPos.y = -item[i].endPoint.y;
                                            loadendPos.rotation = item[i].endPoint.rotation;

                                            var loadCt1 = new ROSLIB.Vector3({
                                                x : parseFloat(item[i].cp1.x), 
                                                y : parseFloat(-item[i].cp1.y)
                                            });
                                            var loadCt2 = new ROSLIB.Vector3({
                                                x : parseFloat(item[i].cp2.x),
                                                y : parseFloat(-item[i].cp2.y)
                                            });
                                            var pointnum = item[i].endPoint.num;
                                            var Curveback = polygon.createBezierShape(loadstartPos, loadendPos, loadCt1, loadCt2);
                                            var Curvebacktext = polygon.createBackBezierText(loadstartPos, loadendPos, loadCt1, loadCt2, item[i].velocity, pointnum);     //此处注意需要修改
                                            polygon.lineContainer.addChild(Curveback, Curvebacktext);
                                            polygon.drawFill();
                                            mapViewer.scene.addChild(polygon);
                                        }
                                    };
                                };
                                console.log("read next point");
                            } else if (json_obj.PATH.item.index) {                                                              
                                startPos.x = item.startPoint.x;
                                startPos.y = item.startPoint.y;        
                                startPos.rotation = item.startPoint.rotation;
                                polygon.addPointPath(startPos, (item.mode == "line") ? 0 : 1, item.velocity);
                                mapViewer.scene.addChild(polygon);                               
                                startPos.x = item.endPoint.x;
                                startPos.y = item.endPoint.y;
                                startPos.rotation = item.endPoint.rotation;
                                polygon.addPointPath(startPos, (item.mode == "line") ? 0 : 1, item.velocity);
                                mapViewer.scene.addChild(polygon);
                            }
                            CurveObj = polygon.getCurvesObj();      //get line json data
                        }
                        if (showMessage)
                            alert("Download Success!");
                        polygon.lineColor = createjs.Graphics.getRGB(100, 100, 255, 1);
                        polygon.lineSize = 0.05;
                    }
                };
            };
            
            function SelectMap() {
                var is_array = Array.isArray(mapList);
                var mapContent = document.getElementById('show-map-id');
                var mapSelecte = document.getElementById('selecte-map-id');
                mapContent.innerHTML = '';

                if (is_array) {
                    for (i=0;i<mapList.length;i++) {
                        console.log("map:", mapList[i]['@id'])
                        var link = document.createElement('a');
                        link.href = '#'; // 或者你想要的链接地址
                        link.className = 'w3-bar-item w3-button';
                        link.textContent = mapList[i]['@id']; // 设置链接文本为 mapList[i]['@id']
                        link.setAttribute('data-index', i); // 设置自定义属性来存储索引
            
                        link.addEventListener('click', function(event) {
                            event.preventDefault();
                            var clickedIndex = parseInt(this.getAttribute('data-index'), 10);
                            console.log('select map:', clickedIndex);
                            mapSelecte.textContent = "当前地图：" +  mapList[clickedIndex]['@id'];
                            DrawPath(clickedIndex);
                        });
            
                        mapContent.appendChild(link);
                    }
                } else {
                    var link = document.createElement('a');
                    link.href = '#'; // 或者你想要的链接地址
                    link.className = 'w3-bar-item w3-button';
                    link.textContent = mapList['@id']; // 设置链接文本为 mapList['@id']
                    mapContent.appendChild(link);
                    mapSelecte.textContent = "当前地图：" +  mapList['@id'];
                    DrawPath(-1);
                }
            }

            function DrawPath(index) {
                // console.log(mapList);
                var pathlist = index >= 0 ? mapList[index].path : mapList.path; //路径数组
                // console.log("path list:", pathlist);
                var pointlist = index >= 0 ? mapList[index].point : mapList.point; //点数组
                // console.log("pathlist.length:", pathlist.length)
                if (pathlist.length > 0) {
                    polygon.removeAllPoint(polygon);
                    console.log("清除所有绘制点和路线"); 

                    for (i = 0; i < pointlist.length; i++) {  // 遍历点数组进行绘制
                        var Pos = new ROSLIB.Vector3();
                        var Pos_name = pointlist[i]['@id']
                        Pos.x = pointlist[i]['@x'];
                        Pos.y = pointlist[i]['@y'];   
                        Pos.rotation = pointlist[i]['@angle'];
                        polygon.drawPoint(Pos, Pos_name);
                        points[parseInt(Pos_name, 10)] = { x: Pos.x, y: Pos.y };
                    };

                    for (j = 0; j < pathlist.length; j++) {
                        // console.log(pathlist[j]);
                        var startPos = pathlist[j]['@startPointId'];
                        var endPos = pathlist[j]['@endPointId'];
                        var pathVelocity = pathlist[j]["@lineVelocity"];

                        var drawstartPos = new ROSLIB.Vector3();
                        var drawendPos = new ROSLIB.Vector3();
                        var direction = 0;
                        for (k = 0; k < pointlist.length; k++) {
                            if(pointlist[k]['@id'] == startPos) {
                                drawstartPos.x = pointlist[k]['@x'];
                                drawstartPos.y = -pointlist[k]['@y'];   
                                drawstartPos.rotation = pointlist[k]['@angle'];
                                // console.log("drawstartPos:", drawstartPos);
                            };

                            if(pointlist[k]['@id'] == endPos) {
                                drawendPos.x = pointlist[k]['@x'];
                                drawendPos.y = -pointlist[k]['@y'];   
                                drawendPos.rotation = pointlist[k]['@angle'];
                                // console.log("drawendPos:", drawendPos);
                            };
                        };
                        
                        if (pathlist[j]['@direction'] == "forward") {
                            direction = 0;
                            if (pathlist[j]['@type'] == "LinePath") {
                                var fordwardline = polygon.createLineShape(drawstartPos, drawendPos);
                                var fordwardlinetext = polygon.drawLineText(drawstartPos, drawendPos, pathlist[j]['@lineVelocity'],
                                                                       pathlist[j]['@id'], pathlist[j]['@startPointId'], 
                                                                       pathlist[j]['@endPointId'], 0);
                                polygon.lineContainer.addChild(fordwardline, fordwardlinetext);
                                polygon.drawFill();
                                mapViewer.scene.addChild(polygon);
                            } else if (pathlist[j]['@type'] == "BezierPath") {
                                var CtBz1 = new ROSLIB.Vector3({
                                    x : parseFloat(pathlist[j]['@control1PosX']), 
                                    y : -parseFloat(pathlist[j]['@control1PosY'])
                                });
                                var CtBz2 = new ROSLIB.Vector3({
                                    x : parseFloat(pathlist[j]['@control2PosX']),
                                    y : -parseFloat(pathlist[j]['@control2PosY'])
                                });
                             
                                var fordwardbezier = polygon.createBezierShape(drawstartPos, drawendPos, CtBz1, CtBz2);
                                var fordwardbeziertext = polygon.drawBezierText(drawstartPos, drawendPos, CtBz1, CtBz2, 
                                                                        pathlist[j]['@lineVelocity'], 
                                                                        pathlist[j]['@id'], pathlist[j]['@startPointId'], 
                                                                        pathlist[j]['@endPointId'], 0);
                                polygon.lineContainer.addChild(fordwardbezier, fordwardbeziertext);
                            }

                        } else if (pathlist[j]['@direction'] == "backward") {
                            direction = 1;
                            polygon.lineColor = createjs.Graphics.getRGB(128, 0, 0, 0.3);
                            polygon.lineSize = 0.03;

                            if (pathlist[j]['@type'] == "LinePath") {
                                var backwardline = polygon.createLineShape(drawstartPos, drawendPos);
                                var backwardlinetext = polygon.drawLineText(drawstartPos, drawendPos, pathlist[j]['@lineVelocity'], 
                                                                       pathlist[j]['@id'], pathlist[j]['@startPointId'], 
                                                                       pathlist[j]['@endPointId'], 1);
                                polygon.lineContainer.addChild(backwardline, backwardlinetext);
                                polygon.drawFill();
                                mapViewer.scene.addChild(polygon);
                            } else if (pathlist[j]['@type'] == "BezierPath") {
                                var CtBz1 = new ROSLIB.Vector3({
                                    x : parseFloat(pathlist[j]['@control1PosX']), 
                                    y : -parseFloat(pathlist[j]['@control1PosY'])
                                });
                                var CtBz2 = new ROSLIB.Vector3({
                                    x : parseFloat(pathlist[j]['@control2PosX']),
                                    y : -parseFloat(pathlist[j]['@control2PosY'])
                                });
                                // console.log(drawstartPos, drawendPos, CtBz1, CtBz2)
                                var backwardbezier = polygon.createBezierShape(drawstartPos, drawendPos, CtBz1, CtBz2);
                                var backwardbeziertext = polygon.drawBezierText(drawstartPos, drawendPos, CtBz1, CtBz2, 
                                                                        pathlist[j]['@lineVelocity'], 
                                                                        pathlist[j]['@id'], pathlist[j]['@startPointId'], 
                                                                        pathlist[j]['@endPointId'], 1);

                                polygon.lineContainer.addChild(backwardbezier, backwardbeziertext);
                            };
                            polygon.lineColor = createjs.Graphics.getRGB(100, 100, 255, 0.6);
                            polygon.lineSize = 0.05;
                        }

                        paths.push({ start: startPos, end: endPos, cost: pathVelocity, direction: direction });
                        CurveObj = polygon.getCurvesObj();
                    // console.log("CurveObj:", CurveObj);      
                    };
                };   
            }

            //地图软件下载路径
            function LoadDrawMap() {
                console.log("Load map.xml");   
                var XHR = new XMLHttpRequest(); 
                XHR.open('GET', "load_path?name=mapTest.xml", true);
                XHR.send();             
                XHR.onreadystatechange = function (){
                    if (XHR.readyState === 4 && XHR.status === 200) {   
                        try {  
                            var res = XHR.responseText;         
                            var obj = eval("(" + res + ")");
                            var json_obj = JSON.parse(obj); 
                            mapList = json_obj.model && json_obj.model.map ? json_obj.model.map : null;
                            // console.log("map list:", mapList);
                            if (mapList !== null) {
                                SelectMap();
                                alert("Download map.xml Success!");
                            } else {
                                alert("加载路线失败！");
                            }
                        } catch (e) {
                            alert("获取AGV路线失败！");
                            console.error("Error parsing JSON response:", e);
                        }
                    };         
                };
            };
            
            //清除所有的路径和点
            function ClearBtnAction() {
                console.log("clear");
                if (CurveObj) {      //定义的变量
                    CurveObj = [];   //空值
                    polygon.removeAllPoint();
                }
            };
            
            //直线闭合。
            function LineLoopAction() {
                console.log("LineLoopAction");
                polygon.addEndLooptPath(0, 2);     //0是直线
                CurveObj = polygon.getCurvesObj();  //获取线的json数据
            };
            //曲线闭合
            function CurveLoopAction() {
                console.log("CurveLoopAction");
                polygon.addEndLooptPath(1, 0.5);     //1是曲线
                CurveObj = polygon.getCurvesObj();
            };
            
            //auto load path，自动加载路径
            //LoadCurveObj(false);
            LoadDrawMap();
            // document.getElementById("loadcurve").addEventListener("click", LoadCurveObj, false);     //下载路线，使用 addEventListener() 方法来向按钮添加点击事件。
            // document.getElementById("loaddrawmap").addEventListener("click", LoadDrawMap, false);
            document.getElementById("clear").addEventListener("click", ClearBtnAction, false);       //清除
            document.getElementById("lineLoop").addEventListener('click', LineLoopAction, false);    //直线闭合
            document.getElementById("curveLoop").addEventListener('click', CurveLoopAction, false);  //曲线闭合
        
            setInterval(function() {
                if (robotTrace && robotTrace.robotMarker) {
                    console.log('Robot marker status:', {
                        exists: !!robotTrace.robotMarker,
                        visible: robotTrace.robotMarker.visible,
                        position: {
                            x: robotTrace.robotMarker.x,
                            y: robotTrace.robotMarker.y
                        },
                        rotation: robotTrace.robotMarker.rotation,
                        parent: robotTrace.robotMarker.parent
                    });
                }
            }, 1000);
        };
        
        //buttons action, enabled edit，进入编辑模式
        function EditBtnAction() {          //编辑按钮调用
            isEditMode = !isEditMode;       //自定义变量， 取反为true。初始值为false 进入编辑模式
            var edit = document.getElementById("edit");         //编辑
            var dropDiv = document.getElementById("drop");
            var toolsBtn = document.getElementById("tools");    //画图工具
            var line = document.getElementById("lineEdit");     //直线工具
            var curve = document.getElementById("curveEdit");   //曲线工具
            if (isEditMode) {
                toolsBtn.disabled = false;      //disabled 属性用于设置或返回文本域是否被禁用。此处可用
                dropDiv.style.visibility = "visible";   //visibility 属性设置元素是否应该是可见的。
                line.style.color = "";
                curve.style.color = "";
                isLineMode = false;
                isCurveMode = false;
                isbackup = false;
                edit.style.color = "green";     //绿色
            } else {
                toolsBtn.disabled = true;       //设置工具不可用。
                dropDiv.style.visibility = "hidden";    //visibility 属性设置元素是否应该是隐藏的。
                isLineMode = false;
                isCurveMode = false;
                isbackup = false;
                edit.style.color = "black";     //黑色
            }
        };

        //直线编辑模式，使用直线工具
        function LineModeAction() {
            var line = document.getElementById("lineEdit");
            var curve = document.getElementById("curveEdit");
            isLineMode = true;
            isCurveMode = false;
            isbackup = false;
            line.style.color = "red";       //红色
            curve.style.color = "";
        };
        //曲线编辑模式，使用曲线工具
        function CurveModeAction() {
            var line = document.getElementById("lineEdit");
            var curve = document.getElementById("curveEdit");
            isLineMode = false;
            isCurveMode = true;
            isbackup = false;
            line.style.color = "";
            curve.style.color = "red";      //红色
        };

        function IsBackup(){
            isbackup = true;
            console.log("start backup");
        }

        //上传操作
        function UploadBtnAction() {
            console.log("Upload Path.");
            var XHR = new XMLHttpRequest();
            var pay_load = { "PATH": (CurveObj ? CurveObj : "NULL") };
            // console.log("pay_load:", pay_load);
            XHR.open('POST', 'upload_path');
            XHR.setRequestHeader('content-type', 'application/json');    //setRequestHeader()是设置HTTP请求头部的方法。此方法必须在open()方法和send()之间调用。  
            XHR.send(JSON.stringify(pay_load));                          //使用 JSON.stringify() 方法将 JavaScript 对象转换为字符串。
            XHR.onreadystatechange = function () {                       //每当 readyState 改变时，就会触发 onreadystatechange 事件。
                if (XHR.readyState === 4 && XHR.status === 200) {
                    var resultText = XHR.responseText;                   //以字符串返回响应数据
                    console.log(resultText);                             //输出
                    var pos = resultText.lastIndexOf("Success!");        //lastIndexOf() 方法可返回一个指定的字符串值最后出现的位置，在一个字符串中的指定位置从后向前搜索。
                    if (pos) {
                        alert("Upload Success!");   //上传路线
                    } else {
                        alert("Upload Failed!");
                    }
                }
            };
        };

        function changeVelocity(val) {              //改变其速度
            var id = parseInt(document.getElementById("pathid").innerHTML.replace(/[^\d]/g, ' '));  //parseInt()函数解析字符串并返回整数。只返回字符串中的第一个数字！如果第一个字符不能转换为数字，parseInt() 返回 NaN。
            console.log("changeVelocity:" + val, "id:" + id);
            if (CurveObj.length >= id) {
                CurveObj[id - 1].velocity = val;
                console.log("change Velobject:", CurveObj[id - 1]);
            }
        };
    </script>
</head>

<body onload="init()">
    <div class="w3-top">
        <div id="orderDialog" class="modal">
            <div class="modal-content w3-card w3-animate-top">
                <h3 id="orderTitle"></h3>

                <label for="actionSelect" >动作行为:</label>
                <select id="actionSelect" class="w3-select">
                    <option value="none">无</option>
                    <option value="load">抬货</option>
                    <option value="unload">卸货</option>
                </select>
                <br> <br>
                <label for="actionParam">动作参数:</label>
                <input type="text" id="actionParam" class="w3-input w3-border" placeholder="输入参数">
                <br>
                <button id="submitButton" class="w3-button w3-border">提交</button>
                <button id="cancelButton" class="w3-button w3-border">取消</button>
            </div>
        </div>

        <!--创建栏，白色，卡片-->
        <div class="w3-bar w3-white w3-card">
            <!--<a> 标签用于定义超链接，作用是从一个页面链接到另一个页面。链接按钮-->
            <a href="#" class="w3-bar-item w3-button w3-wide"><i class="fa fa-paper-plane"></i>导航</a>
            <!--<a> 标签用于定义超链接，作用是从一个页面链接到另一个页面。链接按钮-->
            <a href="#" id="edit" class="w3-bar-item w3-button w3-hide-small" onclick="EditBtnAction()"><i
                    class="fa fa-edit"></i>编辑</a>
            <!--w3-dropdown-hover 类定义了一个 hoverable 下拉元件。onclick是一个事件，当事件被触发时就会执行处理，onclick是一个处理鼠标点击的事件。-->
            <div class="w3-dropdown-hover">
                <button id="tools" class="w3-button" disabled="true"><i class="fa fa-photo"></i>画图工具</button>
                <div id="drop" class="w3-dropdown-content w3-bar-block w3-card-4" style="visibility: hidden;">
                    <a href="#" id="clear" class="w3-bar-item w3-button"><i class="fa fa-eraser"></i>清除</a>
                    <a href="#" id="lineEdit" class="w3-bar-item w3-button" onclick="LineModeAction()"><i class="fa fa-pencil"></i>直线工具</a>
                    <a href="#" id="curveEdit" class="w3-bar-item w3-button" onclick="CurveModeAction()"><i class="fa fa-random"></i>曲线工具</a>
                    <a href="#" id="lineLoop" class="w3-bar-item w3-button"><i class="fa fa-share-alt"></i>直线闭合</a>
                    <a href="#" id="curveLoop" class="w3-bar-item w3-button"><i class="fa fa-share-alt-square"></i>曲线闭合</a>
                </div>
            </div>

            <!--在小屏幕（小于 601px ）上隐藏内容 -->
            <div class="w3-right w3-hide-small">
                <!-- <a href="{{ url_for('home.mapping') }}" class="w3-bar-item w3-button"><i class="fa fa-map"></i>建图</a> -->
                <div class="w3-dropdown-hover">
                    <button class="w3-button" id="selecte-map-id">选译地图</button>
                    <div class="w3-dropdown-content w3-bar-block w3-card-4" id="show-map-id">
                        <!-- 动态内容将插入到这里 -->
                    </div>
                </div>
                <a class="w3-bar-item w3-button" onclick="UploadBtnAction()" href="#"><i class="fa fa-upload"></i>上传路线</a>
              <!--   <a class="w3-bar-item w3-button" href="#" id="loadcurve"><i class="fa fa-download"></i>下载path.xml路线</a>
                <a class="w3-bar-item w3-button" href="#" id="loaddrawmap"><i class="fa fa-download"></i>下载map.xml路线</a > -->
                <a href="{{ url_for('account.logout') }}" class="w3-bar-item w3-button"><i
                        class="fa fa-sign-out"></i>退出</a>
            </div>
        </div>
    </div>
    
    <!--创建一个白色背景容器，进行填充-->
    <div class="w3-container w3-white" style="padding:44px 0px">
        <div class="w3-row-padding">
            <!-- 创建一个容器，按照布局单元格（列）  -->
            <div class="w3-container w3-cell">
                <div id="map"></div>
            </div>
            <!-- 创建一个容器，按照布局单元格（列）  -->
            <div class="w3-container w3-cell">
                <h2>使用说明</h2>
                <p>快捷键:</p>
                <!-- w3-ul类制作基本列表，使用 w3-card 类创建纸质卡  -->
                <ul class="w3-ul w3-card">
                    <li>Ctrl+鼠标左键:缩放地图</li>
                    <li>Shift+鼠标左键:平移地图</li>
                    <li>Alt+鼠标左键:初始化机器人位置</li>
                </ul>
                <p>任务:</p>
                <!-- 状态显示 -->
                <ul id="showTask" class="w3-ul w3-card">
                    <li id="taskCount"></li>
                    <li id="tasklist"></li>
                    <li id="curTask"></li>
                    <li id="taskAction"></li>
                </ul>
                <p>状态:</p>
                <!-- 状态显示 -->
                <ul id="showPeripheral" class="w3-ul w3-card">
                    <li id="vehiclePose"></li>
                    <li id="velocity"></li>
                    <li id="battery">电压:48V 电流:2A</li>
                    <li id="load"></li>
                    <li id="rack"></li>
                </ul>
                <p>信息:</p>
                <!-- 注意此处id信息与基本列表  -->
                <ul id="showPoint" class="w3-ul w3-card" style="display: none;">
                    <li id="pid"></li>
                    <li id="px"></li>
                    <li id="py"></li>
                    <li id="pyaw"></li>
                </ul>
                <ul id="showLine1" class="w3-ul w3-card" style="display: none;">
                    <li id="pathid1"></li>
                    <li id="p1id1"></li>
                    <li id="p1x1"></li>
                    <li id="p1y1"></li>
                    <li id="p1yaw1"></li>
                    <li id="p1id2"></li>
                    <li id="p1x2"></li>
                    <li id="p1y2"></li>
                    <li id="p1yaw2"></li>
                    <li id="cp1x1"></li>
                    <li id="cp1y1"></li>
                    <li id="cp1x2"></li>
                    <li id="cp1y2"></li>
                    <li id="segment1"></li>
                    <li>velocity:<input id="p1vel" type="number" name="velocity" min="0.1" max="1.0" step="0.1" onchange="changeVelocity(this.value)" /></li>  
                </ul>
                <br>
                <ul id="showLine2" class="w3-ul w3-card" style="display: none;">
                    <li id="pathid2"></li>
                    <li id="p2id1"></li>
                    <li id="p2x1"></li>
                    <li id="p2y1"></li>
                    <li id="p2yaw1"></li>
                    <li id="p2id2"></li>
                    <li id="p2x2"></li>
                    <li id="p2y2"></li>
                    <li id="p2yaw2"></li>
                    <li id="cp2x1"></li>
                    <li id="cp2y1"></li>
                    <li id="cp2x2"></li>
                    <li id="cp2y2"></li>
                    <li id="segment2"></li>
                    <li>velocity:<input id="p2vel" type="number" name="velocity" min="0.1" max="1.0" step="0.1" onchange="changeVelocity(this.value)" /></li>
                </ul>
            </div>
        </div>
    </div>

    <!--创建一个白色背景容器，创建纸质卡-->
    <div class="w3-container w3-white w3-card">
        <div class="w3-row-padding">
            <div class="w3-container w3-cell">
                <div class="w3-container w3-cell">
                    <p id="connect" style="color:#d60024;"> Disconnected</p>
                </div>
            </div>
            <!--  <div class="w3-container w3-cell">
                <p id="velocity"></p>
            </div> -->
            <div class="w3-container w3-cell">
                <p id="Pose"></p>
            </div>
            <div class="w3-container w3-cell">
                <p id="score"></p>
            </div>
        </div>
    </div>

</body>

</html>